class SmartLocker
{
  //#define debugEnable
  public:
    SmartLocker(SemaphoreHandle_t* mutex, TickType_t xTicksToWait):m_Mutex(mutex)
    {
      if(m_Mutex != NULL)
      {
        isLocked = xSemaphoreTake(*m_Mutex, xTicksToWait) == pdTRUE;
        #ifdef debugEnable
        if(isLocked)
        {
          Serial.println("xSemaphoreTake is ok.");
        }
        else
        {
          Serial.println("xSemaphoreTake is error.");          
        }
        #endif
      }
    };
    ~SmartLocker()
    {
      if(isLocked)
      {
        xSemaphoreGive(*m_Mutex);
      }
    }
    bool IsLocked() const
    {
      return isLocked;
    }
  private:
    SemaphoreHandle_t* m_Mutex = NULL;
    bool isLocked = false;
};

SemaphoreHandle_t shared_var_mutex = NULL;
int shared_variable = 0;
#define UseMutex

void Task(void *pvParameters)
{
  int task_num = *((int*)pvParameters);
  Serial.printf("%s\n", task_num ? " Starting        |" : "                 | Starting");
  while(1)
  {
    #ifdef UseMutex
    SmartLocker smartLocker(&shared_var_mutex, portMAX_DELAY);
    if(smartLocker.IsLocked())
    {
    #endif
      // Mutex successfully taken
      int new_value = random(1000);

      char str0[32]; sprintf(str0, " %d <- %d      |", shared_variable, new_value);
      char str1[32]; sprintf(str1, "                 | %d <- %d", shared_variable, new_value);
      Serial.printf("%s\n", task_num ? str0 : str1);

      shared_variable = new_value;
      delay(random(100)); // wait random time of max 100 ms - simulating some computation

      sprintf(str0, " R: %d          |", shared_variable);
      sprintf(str1, "                 | R: %d", shared_variable);
      Serial.printf("%s\n", task_num ? str0 : str1);

      if(shared_variable != new_value)
      {
        Serial.printf("%s\n", task_num ? " Mismatch!       |" : "                 | Mismatch!");
      }
    #ifdef UseMutex
    }
    #endif
    delay(10); // Allow other task to be scheduled
  } // Infinite loop
}

void setup()
{
  Serial.begin(115200);
  while(!Serial) delay(100);
  Serial.printf(" Task 0          | Task 1\n");
  shared_var_mutex = xSemaphoreCreateMutex(); // Create the mutex
  // Set up two tasks to run the same function independently.
  static int task_number0 = 0;
  xTaskCreate(
    Task
    ,  "Task 0" // A name just for humans
    ,  2048          // The stack size
    ,  (void*)&task_number0 // Pass reference to a variable describing the task number
    ,  1  // priority
    ,  NULL // Task handle is not used here - simply pass NULL
    );

  static int task_number1 = 1;
  xTaskCreate(
    Task
    ,  "Task 1"
    ,  2048  // Stack size
    ,  (void*)&task_number1 // Pass reference to a variable describing the task number
    ,  1  // Low priority
    ,  NULL // Task handle is not used here - simply pass NULL
    );
}

void loop() {
  // put your main code here, to run repeatedly:
}
